﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MacomberMapSystem.Common.Database;
using MacomberMapSystem.Common.Serialization;
using MacomberMapSystem.Common.User_Interfaces.One_Lines;

namespace MacomberMapSystem.Common.CIM
{
    /// <summary>
    /// This class holds information on a CIM linkage
    /// </summary>
    public class CIM_Link : IEquatable<CIM_Link>, IComparable<CIM_Link>
    {
        #region Variable declarations
        /// <summary>
        /// The type of the outgoing link
        /// </summary>
        public String TypeName
        {
            get { return CIM.Types[TypeByte]; }
        }

        /// <summary>The byte storage for the element</summary>
        internal Byte TypeByte;

        /// <summary>The index of the target</summary>
        public int TargetID;        

        /// <summary>Our temporary element storage for the link, in case it's reused</summary>
        internal CIM_Element _Element = null;

        /// <summary>The CIM connector associated with this link</summary>
        public MM_Database_Model CIM;
        #endregion

        #region Initialization
        /// <summary>
        /// Initialize a new CIM link
        /// </summary>
        /// <param name="BytesStream"></param>
        /// <param name="StartPosition"></param>
        /// <param name="CIM"></param>
        public CIM_Link(Byte[] BytesStream, int StartPosition, MM_Repository CIM)
        {
            this.CIM = CIM as MM_Database_Model;
            TypeByte = BytesStream[StartPosition];
            TargetID = BitConverter.ToInt32(BytesStream, StartPosition + 1);
        }

        /// <summary>
        /// Create a new link from an element
        /// </summary>
        /// <param name="Element"></param>
        /// <returns></returns>
        public static CIM_Link FromElement(CIM_Element Element)
        {
            CIM_Link OutLink = new CIM_Link();
            OutLink.CIM = Element.CIM;
            OutLink.TypeByte = Element.Type;
            OutLink.TargetID = Element.ID;
            OutLink._Element = Element;
            return OutLink;
        }

        /// <summary>
        /// Initialize a new blank CIM link
        /// </summary>
        internal CIM_Link()
        {
        }
      
        /// <summary>
        /// Retrieve the element associated with this link.
        /// </summary>
        public CIM_Element Element
        {
            get
            {
                if (_Element == null)
                    return _Element = CIM.FromID(TargetID,null);
                else
                    return _Element;
            }
        }

        /// <summary>
        /// Return the rdf:ID of our element
        /// </summary>
        public CIM_RdfID rdfID
        {
            get
            {
                if (_Element != null)
                    return _Element.rdfID;
                else
                    return CIM.rdfIDFromID(TargetID);
            }
        }

        /// <summary>
        /// Determine the element type of an element
        /// </summary>
        public MM_OneLine_Element.enumElemTypes ElemType
        {
            get
            {
                if (TypeName == "cim:Disconnector" || TypeName == "cim:GroundDisconnector")
                    return MM_OneLine_Element.enumElemTypes.Switch;
                else if (TypeName == "cim:ACLineSegment")
                    return  MM_OneLine_Element.enumElemTypes.Line;
                else if (TypeName == "cim:SeriesCompensator")
                {                   
                    Dictionary<String, String> XmlVals = Element.Attributes;
                    double r = Convert.ToDouble(XmlVals["cim:SeriesCompensator.r"]);
                    double x = Convert.ToDouble(XmlVals["cim:SeriesCompensator.x"]);
                    if (r > Math.Abs(x / 100))
                        if (x > 0)
                            return MM_OneLine_Element.enumElemTypes.ResistorReactor;
                        else if (x < 0)
                            return MM_OneLine_Element.enumElemTypes.ResistorCapacitor;
                        else
                            return MM_OneLine_Element.enumElemTypes.Resistor;
                    else if (x > 0)
                        return MM_OneLine_Element.enumElemTypes.Reactor;
                    else if (x < 0)
                        return MM_OneLine_Element.enumElemTypes.Capacitor;
                    else
                        return MM_OneLine_Element.enumElemTypes.Reactor;
                    // throw new Exception("Series compensators must have resistance or reactance!");
                }
                else if (TypeName == "cim:Breaker")
                    return MM_OneLine_Element.enumElemTypes.Breaker;
                else if (TypeName == "cim:PowerTransformer")
                    return MM_OneLine_Element.enumElemTypes.Transformer;
                else if (TypeName == "cim:SynchronousMachine")
                    return MM_OneLine_Element.enumElemTypes.Unit;
                else if (TypeName == "etx:EndCap")
                    return MM_OneLine_Element.enumElemTypes.EndCap;
                else if (TypeName == "etx:PricingVector")
                    return MM_OneLine_Element.enumElemTypes.PricingVector;
                else if (TypeName == "cim:ConnectivityNode")
                    return MM_OneLine_Element.enumElemTypes.Node;
                else if (TypeName.EndsWith("StaticVarCompensator"))
                    return MM_OneLine_Element.enumElemTypes.StaticVarCompensator;
                else if (TypeName.EndsWith("Load"))
                    if (Element.ParseSingle("LoadResource", null) == null)
                        return MM_OneLine_Element.enumElemTypes.Load;
                    else
                        return MM_OneLine_Element.enumElemTypes.LAAR;
                else if (TypeName == "cim:ShuntCompensator")
                    if (Convert.ToDouble(Element["NominalMVAR"]) > 0)
                        return MM_OneLine_Element.enumElemTypes.Capacitor;
                    else
                        return MM_OneLine_Element.enumElemTypes.Reactor;
                else
                    return MM_OneLine_Element.enumElemTypes.None;            
            }
        }

        #endregion

        #region Identification
        /// <summary>
        /// Show an easy to read string
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return TypeName + " (" + TargetID.ToString("#,##0") + ")";
        }
        #endregion

        #region Link comparisons
        /// <summary>
        /// Determine whether two links are equal
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(CIM_Link other)
        {
            return this.TargetID.Equals(other.TargetID);
        }

        /// <summary>
        /// Compare two links
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo(CIM_Link other)
        {
            return this.TargetID.CompareTo(other.TargetID);
        }

        /// <summary>
        /// Return the hash code of our index
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return TargetID.GetHashCode();
        }

        #endregion
    }
}
